#include "message_kernel_impl.h"

#include <core/sharedata/login_data.h>
#include <core/system/thread_wrapper.h>
#include <qbnet/source/network_service_impl.h>
#include <qbprotocol/include/rdMessage.h>

#include "process/message_process_impl.h"
#include "sync/message_sync_impl.h"

#ifndef WIN32
#include <arpa/inet.h>
#endif

#include <assert.h>

namespace qb {
CMessageKernel::CMessageKernel()
    : m_spMessageProcess(NULL),
      m_spMessageSync(NULL),
      m_spNetworkService(NULL) {}

CMessageKernel::~CMessageKernel() {}

STDMETHODIMP_(int32_t)
CMessageKernel::Create(THIS_ INetworkService* ins, IMessageSync* sync,
                       IMessageProcess* process) {
  m_spNetworkService = ins;
  m_spMessageSync = sync;
  m_spMessageProcess = process;

  AttachModules();

  return 0;
}

STDMETHODIMP_(int32_t) CMessageKernel::Destory(THIS_) {
  CloseConnect(loginData::instance().ConnectID());

  DetachModules();

  RemoveObserver<IMessageKernelObserver>(m_observers);

  return 0;
}

STDMETHODIMP_(int32_t)
CMessageKernel::RegisterMessageKernelObserver(
    THIS_ IMessageKernelObserver* observer) {
  return RegisterObserver<IMessageKernelObserver>(observer, m_observers);
}

STDMETHODIMP_(int32_t)
CMessageKernel::DeRegisterMessageKernelObserver(
    THIS_ IMessageKernelObserver* observer) {
  return DeRegisterObserver<IMessageKernelObserver>(observer, m_observers);
}

STDMETHODIMP_(int32_t)
CMessageKernel::NotifyNetStatus(THIS_ qb::NetStatus status, short retcode) {
  BList<IMessageKernelObserver*>::iterator iter = m_observers.begin();
  for_each_list(m_observers, iter,
                (iter++)->first->OnRecvNetStatus(status, retcode));

  return 0;
}

STDMETHODIMP_(int32_t)
CMessageKernel::RegisterNetwork(THIS_ int32_t connect_id) {
  QB_CHECK_RETURN_INT_ASSERT1(m_spNetworkService);

  return (static_cast<CNetworkServiceImpl*>(m_spNetworkService))
      ->RegisterNetworkServiceObserver(connect_id, this);
}

STDMETHODIMP_(int32_t)
CMessageKernel::DeRegisterNetwork(THIS_ int32_t connect_id) {
  QB_CHECK_RETURN_INT_ASSERT1(m_spNetworkService);

  return (static_cast<CNetworkServiceImpl*>(m_spNetworkService))
      ->DeRegisterNetworkServiceObserver(connect_id, this);
}

STDMETHODIMP_(int32_t) CMessageKernel::CreateConnect(THIS_) {
  QB_CHECK_RETURN_INT1(m_spNetworkService);

  int32_t connectid = (static_cast<CNetworkServiceImpl*>(m_spNetworkService))
                          ->CreateSocket(kSocketTcp);
  RegisterNetwork(connectid);

  return connectid;
}

STDMETHODIMP_(int32_t)
CMessageKernel::Connect(THIS_ int32_t connect_id, const char* ip,
                        uint32_t port) {
  QB_CHECK_RETURN_INT1(ip);

  if (-1 == connect_id) {
    if (m_spMessageSync) {
      (static_cast<CMessageSyncImpl*>(m_spMessageSync))
          ->PushMessage(0, 0, kLoginConnect,
                        new int16_t(kConnectConnectFailed));
    }

    return -1;
  }

  if (-1 == (static_cast<CNetworkServiceImpl*>(m_spNetworkService))
                ->Connect(connect_id, ip, port)) {
    log_error("can not connect ip:[%s], port:[%d]", ip, port);
    //	Disconnect();

    // 通过MessageSync通知主线程，连接失败
    if (m_spMessageSync) {
      (static_cast<CMessageSyncImpl*>(m_spMessageSync))
          ->PushMessage(0, 0, kLoginConnect,
                        new int16_t(kConnectConnectFailed));
    }
    return -1;
  }

  return 0;
}

STDMETHODIMP_(int32_t)
CMessageKernel::Send(THIS_ int32_t connect_id, const char* buff,
                     uint32_t bytes) {
  QB_CHECK_RETURN_INT1(m_spNetworkService);

  return (static_cast<CNetworkServiceImpl*>(m_spNetworkService))
      ->Send(connect_id, buff, bytes);
}

STDMETHODIMP_(int32_t) CMessageKernel::CloseConnect(THIS_ int32_t connect_id) {
  QB_CHECK_RETURN_INT1(m_spNetworkService);

  DeRegisterNetwork(connect_id);

  if (-1 != connect_id) {
    (static_cast<CNetworkServiceImpl*>(m_spNetworkService))
        ->Disconnect(connect_id);
    (static_cast<CNetworkServiceImpl*>(m_spNetworkService))
        ->DestorySocket(connect_id);
    loginData::instance().ConnectID() = -1;
  }

  return 0;
}

void CMessageKernel::AttachModules() {
  if (m_spMessageSync) {
    (static_cast<CMessageSyncImpl*>(m_spMessageSync))
        ->RegisterMessageObserverTUS(this);
  }
}

void CMessageKernel::DetachModules() {
  if (m_spMessageSync) {
    (static_cast<CMessageSyncImpl*>(m_spMessageSync))
        ->DeRegisterMessageObserverTUS(this);
  }

  m_spMessageProcess = NULL;
  m_spMessageSync = NULL;
  m_spNetworkService = NULL;
}

STDMETHODIMP_(bool)
CMessageKernel::OnConnect(THIS_ int32_t connect_id, int32_t error_id) {
  if (m_spMessageSync && connect_id == loginData::instance().ConnectID()) {
    (static_cast<CMessageSyncImpl*>(m_spMessageSync))
        ->PushMessage(NULL, 0, kLoginConnect, new int32_t(error_id));
    return true;
  }

  return false;
}

STDMETHODIMP_(void)
CMessageKernel::OnReceive(THIS_ int32_t connect_id, MESSAGE_SHARED_PTR msp) {
  QB_CHECK_RETURN_VOID1(m_spMessageProcess);

  if (connect_id == loginData::instance().ConnectID()) {
    (static_cast<CMessageProcessImpl*>(m_spMessageProcess))
        ->PushNetMessage(msp);
  }
}

STDMETHODIMP_(void)
CMessageKernel::OnReceive2(THIS_ int32_t connect_id, const char* buff,
                           uint32_t bytes) {
  QB_CHECK_RETURN_VOID1(m_spMessageSync);

  if (connect_id == loginData::instance().ConnectID()) {
    (static_cast<CMessageSyncImpl*>(m_spMessageSync))->PushMessage(buff, bytes);
  }
}

STDMETHODIMP_(void) CMessageKernel::OnNetData(char* pRecvData, int nDataLen) {
  if (m_spMessageProcess) {
    m_spMessageProcess->PushMainThreadMessage(pRecvData, nDataLen);
  }

  BList<IMessageKernelObserver*>::iterator iter = m_observers.begin();
  for_each_list(m_observers, iter,
                (iter++)->first->OnRecvMsg(pRecvData, nDataLen));
}
}  // namespace qb
